marko
Version:
UI Components + streaming, async, high performance, HTML templating for Node.js and the browser.
236 lines (202 loc) • 6.76 kB
JavaScript
;exports.__esModule = true;exports.default = _default;var _compiler = require("@marko/compiler");
var _babelUtils = require("@marko/compiler/babel-utils");
var _path = require("path");
var _selfClosingTags = _interopRequireDefault(require("self-closing-tags"));
var _htmlOutWrite = _interopRequireDefault(require("../../util/html-out-write"));
var _keyManager = require("../../util/key-manager");
var _withPreviousLocation = _interopRequireDefault(require("../../util/with-previous-location"));
var _attributes = _interopRequireDefault(require("./attributes"));function _interopRequireDefault(e) {return e && e.__esModule ? e : { default: e };}
const EMPTY_OBJECT = {};
/**
* Translates the html streaming version of a standard html element.
*/
function _default(path, isNullable) {
const {
hub: { file },
node
} = path;
const {
key,
name,
body: { body },
handlers
} = node;
const tagProperties = path.node.extra && path.node.extra.properties || [];
const tagDef = (0, _babelUtils.getTagDef)(path);
const meta = file.metadata.marko;
if (tagDef) {
const { parseOptions = EMPTY_OBJECT } = tagDef;
if (parseOptions.import) {
// TODO: the taglib should be updated to support this as a top level option.
file.metadata.marko.deps.push((0, _path.resolve)(tagDef.dir, parseOptions.import));
}
}
if (handlers) {
Object.entries(handlers).forEach(
([eventName, { arguments: args, once }]) => {
const delegateArgs = [_compiler.types.stringLiteral(eventName), args[0]];
// TODO: look into only sending this if once is true.
delegateArgs.push(_compiler.types.booleanLiteral(once));
if (args.length > 1) {
delegateArgs.push(_compiler.types.arrayExpression(args.slice(1)));
}
// TODO: why do we output eventName twice.
tagProperties.push(
_compiler.types.objectProperty(
_compiler.types.stringLiteral(`on${eventName}`),
_compiler.types.callExpression(
_compiler.types.memberExpression(
file._componentDefIdentifier,
_compiler.types.identifier("d")
),
delegateArgs
)
)
);
}
);
}
const isHTML = file.markoOpts.output === "html";
let dataMarko = _compiler.types.stringLiteral("");
if (node.preserveAttrs) {
tagProperties.push(
_compiler.types.objectProperty(
_compiler.types.identifier("pa"),
_compiler.types.objectExpression(
node.preserveAttrs.map((name) =>
_compiler.types.objectProperty(
_compiler.types.isValidIdentifier(name) ?
_compiler.types.identifier(name) :
_compiler.types.stringLiteral(name),
_compiler.types.numericLiteral(1)
)
)
)
)
);
}
if (isHTML) {
if (
!meta.hasStatefulTagParams &&
!meta.hasFunctionEventHandlers && (
meta.hasComponentBrowser || !meta.hasComponent) ||
isPreserved(path))
{
const dataMarkoArgs = [_compiler.types.identifier("out"), file._componentDefIdentifier];
if (tagProperties.length) {
// TODO we should pre evaluate this if it is static.
dataMarkoArgs.push(_compiler.types.objectExpression(tagProperties));
}
if ((0, _keyManager.hasUserKey)(path) || key && node.isPreserved) {
if (dataMarkoArgs.length === 2) {
dataMarkoArgs.push(_compiler.types.numericLiteral(0));
}
dataMarkoArgs.push(key);
}
if (dataMarkoArgs.length > 2) {
dataMarko = _compiler.types.callExpression(
(0, _babelUtils.importDefault)(
file,
"marko/src/runtime/html/helpers/data-marko.js",
"marko_props"
),
dataMarkoArgs
);
}
}
}
const translatedAttrs = (0, _attributes.default)(path, path.get("attributes"));
let isSelfClosing = false;
let openTagEnding = ">";
if (_compiler.types.isStringLiteral(name)) {
if (
tagDef &&
tagDef.htmlType && (
tagDef.htmlType === "svg" || tagDef.htmlType === "math"))
{
if (!body.length) {
isSelfClosing = true;
openTagEnding = " />";
}
} else if (_selfClosingTags.default.voidElements.indexOf(name.value) !== -1) {
isSelfClosing = true;
}
}
const isEmpty = isSelfClosing || !body.length;
let writeStartNode = (0, _babelUtils.normalizeTemplateString)`<${name}${dataMarko}${translatedAttrs}${openTagEnding}`;
writeStartNode = (0, _withPreviousLocation.default)(
isEmpty && !isSelfClosing ?
(0, _htmlOutWrite.default)`${writeStartNode}</${name}>` :
(0, _htmlOutWrite.default)`${writeStartNode}`,
name
);
if (isNullable) {
writeStartNode = _compiler.types.ifStatement(name, writeStartNode);
if (!isEmpty) {
writeStartNode.alternate = _compiler.types.expressionStatement(
_compiler.types.callExpression(
_compiler.types.memberExpression(_compiler.types.identifier("out"), _compiler.types.identifier("bf")),
[
(0, _babelUtils.normalizeTemplateString)`f_${key}`,
file._componentInstanceIdentifier,
_compiler.types.numericLiteral(1)]
)
);
}
}
if (isEmpty) {
path.replaceWith(writeStartNode);
return;
}
let needsBlock;
let needsIIFE;
for (const childNode of body) {
if (_compiler.types.isVariableDeclaration(childNode)) {
if (childNode.kind === "const" || childNode.kind === "let") {
needsBlock = true;
} else {
needsIIFE = true;
}
break;
}
}
let writeEndNode = (0, _htmlOutWrite.default)`</${name}>`;
if (isNullable) {
writeEndNode = _compiler.types.ifStatement(
name,
writeEndNode,
_compiler.types.expressionStatement(
_compiler.types.callExpression(
_compiler.types.memberExpression(_compiler.types.identifier("out"), _compiler.types.identifier("ef")),
[]
)
)
);
}
path.replaceWithMultiple(
[writeStartNode].
concat(
needsIIFE ?
_compiler.types.expressionStatement(
_compiler.types.callExpression(
_compiler.types.arrowFunctionExpression([], _compiler.types.blockStatement(body)),
[]
)
) :
needsBlock ?
_compiler.types.blockStatement(body) :
body
).
concat(writeEndNode)
);
}
function isPreserved(path) {
let parentTag = path;
do {
parentTag = parentTag.parentPath.parentPath;
if (parentTag.get("isPreserved").node === true) {
return true;
}
} while (_compiler.types.isMarkoTag(parentTag));
return false;
}